home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 21 / AACD 21.iso / AACD / Utilities / Ghostscript / src / imain.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-01-01  |  20.1 KB  |  729 lines

  1. /* Copyright (C) 1989, 1996, 1997, 1998, 1999 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of AFPL Ghostscript.
  4.   
  5.   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
  6.   distributor accepts any responsibility for the consequences of using it, or
  7.   for whether it serves any particular purpose or works at all, unless he or
  8.   she says so in writing.  Refer to the Aladdin Free Public License (the
  9.   "License") for full details.
  10.   
  11.   Every copy of AFPL Ghostscript must include a copy of the License, normally
  12.   in a plain ASCII text file named PUBLIC.  The License grants you the right
  13.   to copy, modify and redistribute AFPL Ghostscript, but only under certain
  14.   conditions described in the License.  Among other things, the License
  15.   requires that the copyright notice and this notice be preserved on all
  16.   copies.
  17. */
  18.  
  19. /*$Id: imain.c,v 1.2.2.1 2000/11/09 22:39:35 rayjj Exp $ */
  20. /* Common support for interpreter front ends */
  21. #include "memory_.h"
  22. #include "string_.h"
  23. /* Capture stdin/out/err before gs.h redefines them. */
  24. #include <stdio.h>
  25. private void
  26. set_stdfiles(FILE * stdfiles[3])
  27. {
  28.     stdfiles[0] = stdin;
  29.     stdfiles[1] = stdout;
  30.     stdfiles[2] = stderr;
  31. }
  32. #include "ghost.h"
  33. #include "gp.h"
  34. #include "gscdefs.h"        /* for gs_init_file */
  35. #include "gslib.h"
  36. #include "gsmatrix.h"        /* for gxdevice.h */
  37. #include "gsutil.h"        /* for bytes_compare */
  38. #include "gxdevice.h"
  39. #include "errors.h"
  40. #include "oper.h"
  41. #include "iconf.h"        /* for gs_init_* imports */
  42. #include "idebug.h"
  43. #include "idict.h"
  44. #include "iname.h"        /* for name_init */
  45. #include "dstack.h"
  46. #include "estack.h"
  47. #include "ostack.h"        /* put here for files.h */
  48. #include "stream.h"        /* for files.h */
  49. #include "files.h"
  50. #include "ialloc.h"
  51. #include "iinit.h"
  52. #include "strimpl.h"        /* for sfilter.h */
  53. #include "sfilter.h"        /* for iscan.h */
  54. #include "iscan.h"
  55. #include "main.h"
  56. #include "store.h"
  57. #include "isave.h"        /* for prototypes */
  58. #include "interp.h"
  59. #include "ivmspace.h"
  60.  
  61. /* ------ Exported data ------ */
  62.  
  63. /* Define the default instance of the interpreter. */
  64. /* Currently, this is the *only possible* instance, because most of */
  65. /* the places that need to take an explicit instance argument don't. */
  66. private gs_main_instance the_gs_main_instance;
  67. gs_main_instance *
  68. gs_main_instance_default(void)
  69. {
  70.     /* Determine whether the instance has been initialized. */
  71.     if (the_gs_main_instance.memory_chunk_size == 0)
  72.     the_gs_main_instance = gs_main_instance_init_values;
  73.     return &the_gs_main_instance;
  74. }
  75.  
  76. /* The only reason we export gs_exit_status is so that window systems */
  77. /* with alert boxes can know whether to pause before exiting if */
  78. /* the program terminates with an error.  There must be a better way .... */
  79. int gs_exit_status;
  80.  
  81. /* Define the interpreter's name table.  We'll move it somewhere better */
  82. /* eventually.... */
  83. name_table *the_gs_name_table;
  84.  
  85. /* ------ Forward references ------ */
  86.  
  87. private int gs_run_init_file(P3(gs_main_instance *, int *, ref *));
  88. private void print_resource_usage(P3(const gs_main_instance *,
  89.                      gs_dual_memory_t *,
  90.                      const char *));
  91.  
  92. /* ------ Initialization ------ */
  93.  
  94. /* Save the real stdio files. */
  95. void
  96. gs_get_real_stdio(FILE * stdfiles[3])
  97. {
  98.     set_stdfiles(stdfiles);
  99. }
  100.  
  101. /* Initialization to be done before anything else. */
  102. int
  103. gs_main_init0(gs_main_instance * minst, FILE * in, FILE * out, FILE * err,
  104.           int max_lib_paths)
  105. {
  106.     gs_memory_t *heap;
  107.     ref *paths;
  108.  
  109.     /* Set our versions of stdin/out/err. */
  110.     gs_stdin = minst->fstdin = in;
  111.     gs_stdout = minst->fstdout = out;
  112.     gs_stderr = minst->fstderr = err;
  113.     /* Do platform-dependent initialization. */
  114.     /* We have to do this as the very first thing, */
  115.     /* because it detects attempts to run 80N86 executables (N>0) */
  116.     /* on incompatible processors. */
  117.     gp_init();
  118.     gp_get_usertime(minst->base_time);
  119.     /* Initialize the imager. */
  120.     heap = gs_lib_init0(gs_stdout);
  121.     if (heap == 0)
  122.     return_error(e_VMerror);
  123.     minst->heap = heap;
  124.     /* Initialize the file search paths. */
  125.     paths = (ref *) gs_alloc_byte_array(heap, max_lib_paths, sizeof(ref),
  126.                     "lib_path array");
  127.     if (paths == 0) {
  128.     gs_lib_finit(1, e_VMerror);
  129.     return_error(e_VMerror);
  130.     }
  131.     make_array(&minst->lib_path.container, avm_foreign, max_lib_paths,
  132.            (ref *) gs_alloc_byte_array(heap, max_lib_paths, sizeof(ref),
  133.                        "lib_path array"));
  134.     make_array(&minst->lib_path.list, avm_foreign | a_readonly, 0,
  135.            minst->lib_path.container.value.refs);
  136.     minst->lib_path.env = 0;
  137.     minst->lib_path.final = 0;
  138.     minst->lib_path.count = 0;
  139.     minst->user_errors = 1;
  140.     minst->init_done = 0;
  141.     return 0;
  142. }
  143.  
  144. /* Initialization to be done before constructing any objects. */
  145. int
  146. gs_main_init1(gs_main_instance * minst)
  147. {
  148.     if (minst->init_done < 1) {
  149.     gs_dual_memory_t idmem;
  150.     int code =
  151.         ialloc_init(&idmem, (gs_raw_memory_t *)&gs_memory_default,
  152.             minst->memory_chunk_size, gs_have_level2());
  153.  
  154.     if (code < 0)
  155.         return code;
  156.     code = gs_lib_init1((gs_memory_t *)idmem.space_system);
  157.     if (code < 0)
  158.         return code;
  159.     alloc_save_init(&idmem);
  160.     {
  161.         gs_memory_t *mem = (gs_memory_t *)idmem.space_system;
  162.         name_table *nt = names_init(minst->name_table_size,
  163.                     idmem.space_system);
  164.  
  165.         if (nt == 0)
  166.         return_error(e_VMerror);
  167.         the_gs_name_table = nt;
  168.         code = gs_register_struct_root(mem, NULL,
  169.                        (void **)&the_gs_name_table,
  170.                        "the_gs_name_table");
  171.         if (code < 0)
  172.         return code;
  173.     }
  174.     code = obj_init(&minst->i_ctx_p, &idmem);  /* requires name_init */
  175.     if (code < 0)
  176.         return code;
  177.     minst->init_done = 1;
  178.     }
  179.     return 0;
  180. }
  181.  
  182. /* Initialization to be done before running any files. */
  183. private void
  184. init2_make_string_array(i_ctx_t *i_ctx_p, const ref * srefs, const char *aname)
  185. {
  186.     const ref *ifp = srefs;
  187.     ref ifa;
  188.  
  189.     for (; ifp->value.bytes != 0; ifp++);
  190.     make_tasv(&ifa, t_array, a_readonly | avm_foreign,
  191.           ifp - srefs, const_refs, srefs);
  192.     initial_enter_name(aname, &ifa);
  193. }
  194. int
  195. gs_main_init2(gs_main_instance * minst)
  196. {
  197.     i_ctx_t *i_ctx_p;
  198.     int code = gs_main_init1(minst);
  199.  
  200.     if (code < 0)
  201.     return code;
  202.     i_ctx_p = minst->i_ctx_p;
  203.     if (minst->init_done < 2) {
  204.     int code, exit_code;
  205.     ref error_object;
  206.  
  207.     code = zop_init(i_ctx_p);
  208.     if (code < 0)
  209.         return code;
  210.     {
  211.         /*
  212.          * gs_iodev_init has to be called here (late), rather than
  213.          * with the rest of the library init procedures, because of
  214.          * some hacks specific to MS Windows for patching the
  215.          * stdxxx IODevices.
  216.          */
  217.         extern init_proc(gs_iodev_init);
  218.  
  219.         code = gs_iodev_init(imemory);
  220.         if (code < 0)
  221.         return code;
  222.     }
  223.     code = op_init(i_ctx_p);    /* requires obj_init */
  224.     if (code < 0)
  225.         return code;
  226.  
  227.     /* Set up the array of additional initialization files. */
  228.     init2_make_string_array(i_ctx_p, gs_init_file_array, "INITFILES");
  229.     /* Set up the array of emulator names. */
  230.     init2_make_string_array(i_ctx_p, gs_emulator_name_array, "EMULATORS");
  231.     /* Pass the search path. */
  232.     code = initial_enter_name("LIBPATH", &minst->lib_path.list);
  233.     if (code < 0)
  234.         return code;
  235.  
  236.     /* Execute the standard initialization file. */
  237.     code = gs_run_init_file(minst, &exit_code, &error_object);
  238.     if (code < 0)
  239.         return code;
  240.     minst->init_done = 2;
  241.     i_ctx_p = minst->i_ctx_p; /* init file may change it */
  242.     }
  243.     if (gs_debug_c(':'))
  244.     print_resource_usage(minst, &gs_imemory, "Start");
  245.     gp_readline_init(&minst->readline_data, imemory_system);
  246.     return 0;
  247. }
  248.  
  249. /* ------ Search paths ------ */
  250.  
  251. /* Internal routine to add a set of directories to a search list. */
  252. /* Returns 0 or an error code. */
  253. private int
  254. file_path_add(gs_file_path * pfp, const char *dirs)
  255. {
  256.     uint len = r_size(&pfp->list);
  257.     const char *dpath = dirs;
  258.  
  259.     if (dirs == 0)
  260.     return 0;
  261.     for (;;) {            /* Find the end of the next directory name. */
  262.     const char *npath = dpath;
  263.  
  264.     while (*npath != 0 && *npath != gp_file_name_list_separator)
  265.         npath++;
  266.     if (npath > dpath) {
  267.         if (len == r_size(&pfp->container))
  268.         return_error(e_limitcheck);
  269.         make_const_string(&pfp->container.value.refs[len],
  270.                   avm_foreign | a_readonly,
  271.                   npath - dpath, (const byte *)dpath);
  272.         ++len;
  273.     }
  274.     if (!*npath)
  275.         break;
  276.     dpath = npath + 1;
  277.     }
  278.     r_set_size(&pfp->list, len);
  279.     return 0;
  280. }
  281.  
  282. /* Add a library search path to the list. */
  283. int
  284. gs_main_add_lib_path(gs_main_instance * minst, const char *lpath)
  285. {
  286.     /* Account for the possibility that the first element */
  287.     /* is gp_current_directory name added by set_lib_paths. */
  288.     int first_is_here =
  289.     (r_size(&minst->lib_path.list) != 0 &&
  290.      minst->lib_path.container.value.refs[0].value.bytes ==
  291.      (const byte *)gp_current_directory_name ? 1 : 0);
  292.     int code;
  293.  
  294.     r_set_size(&minst->lib_path.list, minst->lib_path.count +
  295.            first_is_here);
  296.     code = file_path_add(&minst->lib_path, lpath);
  297.     minst->lib_path.count = r_size(&minst->lib_path.list) - first_is_here;
  298.     if (code < 0)
  299.     return code;
  300.     return gs_main_set_lib_paths(minst);
  301. }
  302.  
  303. /* ------ Execution ------ */
  304.  
  305. /* Complete the list of library search paths. */
  306. /* This may involve adding or removing the current directory */
  307. /* as the first element. */
  308. int
  309. gs_main_set_lib_paths(gs_main_instance * minst)
  310. {
  311.     ref *paths = minst->lib_path.container.value.refs;
  312.     int first_is_here =
  313.     (r_size(&minst->lib_path.list) != 0 &&
  314.      paths[0].value.bytes == (const byte *)gp_current_directory_name ? 1 : 0);
  315.     int count = minst->lib_path.count;
  316.     int code = 0;
  317.  
  318.     if (minst->search_here_first) {
  319.     if (!(first_is_here ||
  320.           (r_size(&minst->lib_path.list) != 0 &&
  321.            !bytes_compare((const byte *)gp_current_directory_name,
  322.                   strlen(gp_current_directory_name),
  323.                   paths[0].value.bytes,
  324.                   r_size(&paths[0]))))
  325.         ) {
  326.         memmove(paths + 1, paths, count * sizeof(*paths));
  327.         make_const_string(paths, avm_foreign | a_readonly,
  328.                   strlen(gp_current_directory_name),
  329.                   (const byte *)gp_current_directory_name);
  330.     }
  331.     } else {
  332.     if (first_is_here)
  333.         memmove(paths, paths + 1, count * sizeof(*paths));
  334.     }
  335.     r_set_size(&minst->lib_path.list,
  336.            count + (minst->search_here_first ? 1 : 0));
  337.     if (minst->lib_path.env != 0)
  338.     code = file_path_add(&minst->lib_path, minst->lib_path.env);
  339.     if (minst->lib_path.final != 0 && code >= 0)
  340.     code = file_path_add(&minst->lib_path, minst->lib_path.final);
  341.     return code;
  342. }
  343.  
  344. /* Open a file, using the search paths. */
  345. int
  346. gs_main_lib_open(gs_main_instance * minst, const char *file_name, ref * pfile)
  347. {
  348.     /* This is a separate procedure only to avoid tying up */
  349.     /* extra stack space while running the file. */
  350.     i_ctx_t *i_ctx_p = minst->i_ctx_p;
  351. #define maxfn 200
  352.     byte fn[maxfn];
  353.     uint len;
  354.  
  355.     return lib_file_open(file_name, strlen(file_name), fn, maxfn,
  356.              &len, pfile, imemory);
  357. }
  358.  
  359. /* Open and execute a file. */
  360. int
  361. gs_main_run_file(gs_main_instance * minst, const char *file_name, int user_errors, int *pexit_code, ref * perror_object)
  362. {
  363.     ref initial_file;
  364.     int code = gs_main_run_file_open(minst, file_name, &initial_file);
  365.  
  366.     if (code < 0)
  367.     return code;
  368.     return gs_interpret(&minst->i_ctx_p, &initial_file, user_errors,
  369.             pexit_code, perror_object);
  370. }
  371. int
  372. gs_main_run_file_open(gs_main_instance * minst, const char *file_name, ref * pfref)
  373. {
  374.     gs_main_set_lib_paths(minst);
  375.     if (gs_main_lib_open(minst, file_name, pfref) < 0) {
  376.     eprintf1("Can't find initialization file %s.\n", file_name);
  377.     return_error(e_Fatal);
  378.     }
  379.     r_set_attrs(pfref, a_execute + a_executable);
  380.     return 0;
  381. }
  382.  
  383. /* Open and run the very first initialization file. */
  384. private int
  385. gs_run_init_file(gs_main_instance * minst, int *pexit_code, ref * perror_object)
  386. {
  387.     i_ctx_t *i_ctx_p = minst->i_ctx_p;
  388.     ref ifile;
  389.     ref first_token;
  390.     int code;
  391.     scanner_state state;
  392.  
  393.     gs_main_set_lib_paths(minst);
  394.     if (gs_init_string_sizeof == 0) {    /* Read from gs_init_file. */
  395.     code = gs_main_run_file_open(minst, gs_init_file, &ifile);
  396.     } else {            /* Read from gs_init_string. */
  397.     code = file_read_string(gs_init_string, gs_init_string_sizeof, &ifile,
  398.                 iimemory);
  399.     }
  400.     if (code < 0) {
  401.     *pexit_code = 255;
  402.     return code;
  403.     }
  404.     /* Check to make sure the first token is an integer */
  405.     /* (for the version number check.) */
  406.     scanner_state_init(&state, false);
  407.     code = scan_token(i_ctx_p, ifile.value.pfile, &first_token,
  408.               &state);
  409.     if (code != 0 || !r_has_type(&first_token, t_integer)) {
  410.     eprintf1("Initialization file %s does not begin with an integer.\n", gs_init_file);
  411.     *pexit_code = 255;
  412.     return_error(e_Fatal);
  413.     }
  414.     *++osp = first_token;
  415.     r_set_attrs(&ifile, a_executable);
  416.     return gs_interpret(&minst->i_ctx_p, &ifile, minst->user_errors,
  417.             pexit_code, perror_object);
  418. }
  419.  
  420. /* Run a string. */
  421. int
  422. gs_main_run_string(gs_main_instance * minst, const char *str, int user_errors,
  423.            int *pexit_code, ref * perror_object)
  424. {
  425.     return gs_main_run_string_with_length(minst, str, (uint) strlen(str),
  426.                       user_errors,
  427.                       pexit_code, perror_object);
  428. }
  429. int
  430. gs_main_run_string_with_length(gs_main_instance * minst, const char *str,
  431.      uint length, int user_errors, int *pexit_code, ref * perror_object)
  432. {
  433.     int code;
  434.  
  435.     code = gs_main_run_string_begin(minst, user_errors,
  436.                     pexit_code, perror_object);
  437.     if (code < 0)
  438.     return code;
  439.     code = gs_main_run_string_continue(minst, str, length, user_errors,
  440.                        pexit_code, perror_object);
  441.     if (code != e_NeedInput)
  442.     return code;
  443.     return gs_main_run_string_end(minst, user_errors,
  444.                   pexit_code, perror_object);
  445. }
  446.  
  447. /* Set up for a suspendable run_string. */
  448. int
  449. gs_main_run_string_begin(gs_main_instance * minst, int user_errors,
  450.              int *pexit_code, ref * perror_object)
  451. {
  452.     const char *setup = ".runstringbegin";
  453.     ref rstr;
  454.     int code;
  455.  
  456.     gs_main_set_lib_paths(minst);
  457.     make_const_string(&rstr, avm_foreign | a_readonly | a_executable,
  458.               strlen(setup), (const byte *)setup);
  459.     code = gs_interpret(&minst->i_ctx_p, &rstr, user_errors, pexit_code,
  460.             perror_object);
  461.     return (code == e_NeedInput ? 0 : code == 0 ? e_Fatal : code);
  462. }
  463. /* Continue running a string with the option of suspending. */
  464. int
  465. gs_main_run_string_continue(gs_main_instance * minst, const char *str,
  466.      uint length, int user_errors, int *pexit_code, ref * perror_object)
  467. {
  468.     ref rstr;
  469.  
  470.     if (length == 0)
  471.     return 0;        /* empty string signals EOF */
  472.     make_const_string(&rstr, avm_foreign | a_readonly, length,
  473.               (const byte *)str);
  474.     return gs_interpret(&minst->i_ctx_p, &rstr, user_errors, pexit_code,
  475.             perror_object);
  476. }
  477. /* Signal EOF when suspended. */
  478. int
  479. gs_main_run_string_end(gs_main_instance * minst, int user_errors,
  480.                int *pexit_code, ref * perror_object)
  481. {
  482.     ref rstr;
  483.  
  484.     make_empty_const_string(&rstr, avm_foreign | a_readonly);
  485.     return gs_interpret(&minst->i_ctx_p, &rstr, user_errors, pexit_code,
  486.             perror_object);
  487. }
  488.  
  489. /* ------ Operand stack access ------ */
  490.  
  491. /* These are built for comfort, not for speed. */
  492.  
  493. private int
  494. push_value(gs_main_instance *minst, ref * pvalue)
  495. {
  496.     i_ctx_t *i_ctx_p = minst->i_ctx_p;
  497.     int code = ref_stack_push(&o_stack, 1);
  498.  
  499.     if (code < 0)
  500.     return code;
  501.     *ref_stack_index(&o_stack, 0L) = *pvalue;
  502.     return 0;
  503. }
  504.  
  505. int
  506. gs_push_boolean(gs_main_instance * minst, bool value)
  507. {
  508.     ref vref;
  509.  
  510.     make_bool(&vref, value);
  511.     return push_value(minst, &vref);
  512. }
  513.  
  514. int
  515. gs_push_integer(gs_main_instance * minst, long value)
  516. {
  517.     ref vref;
  518.  
  519.     make_int(&vref, value);
  520.     return push_value(minst, &vref);
  521. }
  522.  
  523. int
  524. gs_push_real(gs_main_instance * minst, floatp value)
  525. {
  526.     ref vref;
  527.  
  528.     make_real(&vref, value);
  529.     return push_value(minst, &vref);
  530. }
  531.  
  532. int
  533. gs_push_string(gs_main_instance * minst, byte * chars, uint length,
  534.            bool read_only)
  535. {
  536.     ref vref;
  537.  
  538.     make_string(&vref, avm_foreign | (read_only ? a_readonly : a_all),
  539.         length, (byte *) chars);
  540.     return push_value(minst, &vref);
  541. }
  542.  
  543. private int
  544. pop_value(i_ctx_t *i_ctx_p, ref * pvalue)
  545. {
  546.     if (!ref_stack_count(&o_stack))
  547.     return_error(e_stackunderflow);
  548.     *pvalue = *ref_stack_index(&o_stack, 0L);
  549.     return 0;
  550. }
  551.  
  552. int
  553. gs_pop_boolean(gs_main_instance * minst, bool * result)
  554. {
  555.     i_ctx_t *i_ctx_p = minst->i_ctx_p;
  556.     ref vref;
  557.     int code = pop_value(i_ctx_p, &vref);
  558.  
  559.     if (code < 0)
  560.     return code;
  561.     check_type_only(vref, t_boolean);
  562.     *result = vref.value.boolval;
  563.     ref_stack_pop(&o_stack, 1);
  564.     return 0;
  565. }
  566.  
  567. int
  568. gs_pop_integer(gs_main_instance * minst, long *result)
  569. {
  570.     i_ctx_t *i_ctx_p = minst->i_ctx_p;
  571.     ref vref;
  572.     int code = pop_value(i_ctx_p, &vref);
  573.  
  574.     if (code < 0)
  575.     return code;
  576.     check_type_only(vref, t_integer);
  577.     *result = vref.value.intval;
  578.     ref_stack_pop(&o_stack, 1);
  579.     return 0;
  580. }
  581.  
  582. int
  583. gs_pop_real(gs_main_instance * minst, float *result)
  584. {
  585.     i_ctx_t *i_ctx_p = minst->i_ctx_p;
  586.     ref vref;
  587.     int code = pop_value(i_ctx_p, &vref);
  588.  
  589.     if (code < 0)
  590.     return code;
  591.     switch (r_type(&vref)) {
  592.     case t_real:
  593.         *result = vref.value.realval;
  594.         break;
  595.     case t_integer:
  596.         *result = vref.value.intval;
  597.         break;
  598.     default:
  599.         return_error(e_typecheck);
  600.     }
  601.     ref_stack_pop(&o_stack, 1);
  602.     return 0;
  603. }
  604.  
  605. int
  606. gs_pop_string(gs_main_instance * minst, gs_string * result)
  607. {
  608.     i_ctx_t *i_ctx_p = minst->i_ctx_p;
  609.     ref vref;
  610.     int code = pop_value(i_ctx_p, &vref);
  611.  
  612.     if (code < 0)
  613.     return code;
  614.     switch (r_type(&vref)) {
  615.     case t_name:
  616.         name_string_ref(&vref, &vref);
  617.         code = 1;
  618.         goto rstr;
  619.     case t_string:
  620.         code = (r_has_attr(&vref, a_write) ? 0 : 1);
  621.       rstr:result->data = vref.value.bytes;
  622.         result->size = r_size(&vref);
  623.         break;
  624.     default:
  625.         return_error(e_typecheck);
  626.     }
  627.     ref_stack_pop(&o_stack, 1);
  628.     return code;
  629. }
  630.  
  631. /* ------ Termination ------ */
  632.  
  633. /* Free all resources and exit. */
  634. void
  635. gs_main_finit(gs_main_instance * minst, int exit_status, int code)
  636. {
  637.     i_ctx_t *i_ctx_p = minst->i_ctx_p;
  638.  
  639.     /*
  640.      * Previous versions of this code closed the devices in the
  641.      * device list here.  Since these devices are now prototypes,
  642.      * they cannot be opened, so they do not need to be closed;
  643.      * alloc_restore_all will close dynamically allocated devices.
  644.      */
  645.     gs_exit_status = exit_status;    /* see above */
  646.     gp_readline_finit(minst->readline_data);
  647.     if (gs_debug_c(':'))
  648.     print_resource_usage(minst, &gs_imemory, "Final");
  649.     /* Do the equivalent of a restore "past the bottom". */
  650.     /* This will release all memory, close all open files, etc. */
  651.     if (minst->init_done >= 1)
  652.     alloc_restore_all(idmemory);
  653.     gs_lib_finit(exit_status, code);
  654. }
  655. void
  656. gs_exit_with_code(int exit_status, int code)
  657. {
  658.     gs_finit(exit_status, code);
  659.     gp_do_exit(exit_status);
  660. }
  661. void
  662. gs_exit(int exit_status)
  663. {
  664.     gs_exit_with_code(exit_status, 0);
  665. }
  666.  
  667. /* ------ Debugging ------ */
  668.  
  669. /* Print resource usage statistics. */
  670. private void
  671. print_resource_usage(const gs_main_instance * minst, gs_dual_memory_t * dmem,
  672.              const char *msg)
  673. {
  674.     ulong allocated = 0, used = 0;
  675.     long utime[2];
  676.  
  677.     gp_get_usertime(utime);
  678.     {
  679.     int i;
  680.  
  681.     for (i = 0; i < countof(dmem->spaces_indexed); ++i) {
  682.         gs_ref_memory_t *mem = dmem->spaces_indexed[i];
  683.  
  684.         if (mem != 0 && (i == 0 || mem != dmem->spaces_indexed[i - 1])) {
  685.         gs_memory_status_t status;
  686.         gs_ref_memory_t *mem_stable =
  687.             (gs_ref_memory_t *)gs_memory_stable((gs_memory_t *)mem);
  688.  
  689.         gs_memory_status((gs_memory_t *)mem, &status);
  690.         allocated += status.allocated;
  691.         used += status.used;
  692.         if (mem_stable != mem) {
  693.             gs_memory_status((gs_memory_t *)mem_stable, &status);
  694.             allocated += status.allocated;
  695.             used += status.used;
  696.         }
  697.         }
  698.     }
  699.     }
  700.     dprintf4("%% %s time = %g, memory allocated = %lu, used = %lu\n",
  701.          msg, utime[0] - minst->base_time[0] +
  702.          (utime[1] - minst->base_time[1]) / 1000000000.0,
  703.          allocated, used);
  704. }
  705.  
  706. /* Dump the stacks after interpretation */
  707. void
  708. gs_main_dump_stack(gs_main_instance *minst, int code, ref * perror_object)
  709. {
  710.     i_ctx_t *i_ctx_p = minst->i_ctx_p;
  711.  
  712.     zflush(i_ctx_p);        /* force out buffered output */
  713.     dprintf1("\nUnexpected interpreter error %d.\n", code);
  714.     if (perror_object != 0) {
  715.     dputs("Error object: ");
  716.     debug_print_ref(perror_object);
  717.     dputc('\n');
  718.     }
  719.     debug_dump_stack(&o_stack, "Operand stack");
  720.     debug_dump_stack(&e_stack, "Execution stack");
  721.     debug_dump_stack(&d_stack, "Dictionary stack");
  722. }
  723. /* Backward compatibility */
  724. void
  725. gs_debug_dump_stack(int code, ref * perror_object)
  726. {
  727.     gs_main_dump_stack(gs_main_instance_default(), code, perror_object);
  728. }
  729.